#include <ddm/if/gen-cpp2/DdmCtrlAsyncClient.h>
#include <folly/init/Init.h>
#include <openr/common/NetworkUtil.h>
#include <openr/common/OpenrClient.h>
#include <openr/common/Util.h>
#include <openr/if/gen-cpp2/OpenrCtrlCppAsyncClient.h>

#include <fstream>
#include <iostream>
#include <vector>

using namespace openr;
using namespace std;

// DEFINE_string(module, "::1", "Host to connect to");

vector<string> split(string s, string delimiter) {
  size_t pos_start = 0, pos_end, delim_len = delimiter.length();
  string token;
  vector<string> res;

  while ((pos_end = s.find(delimiter, pos_start)) != string::npos) {
    token = s.substr(pos_start, pos_end - pos_start);
    pos_start = pos_end + delim_len;
    res.push_back(token);
  }

  res.push_back(s.substr(pos_start));
  return res;
}

void submitReq(std::string name, std::string prefix) {
  std::cout << "Submitting requirement" << std::endl;
  folly::EventBase evb;
  std::thread evbThread([&evb]() { evb.loopForever(); });
  evb.waitUntilRunning();
  auto client =
      openr::getOpenrCtrlPlainTextClient<openr::thrift::OpenrCtrlCppAsyncClient,
                                         apache::thrift::RocketClientChannel>(
          evb, folly::IPAddress("::1"), 2018, std::chrono::milliseconds(1000),
          std::chrono::milliseconds(5000));
  ddm::thrift::Requirement req;
  req.name() = name;
  openr::thrift::IpPrefix hs = toIpPrefix(prefix);
  req.prefix() = hs;
  openr::thrift::KeySetParams ksparams;

  apache::thrift::CompactSerializer serializer;
  ksparams.keyVals()["req:" + *req.name()] =
      openr::createThriftValue(1, "ddm", writeThriftObjStr(req, serializer));
  client->semifuture_setKvStoreKeyVals(ksparams, "0");
  evb.terminateLoopSoon();
  evbThread.join();
}

void submitReqFile(std::string filename) {
  std::string reqName;
  std::string prefix;
  fstream newfile;
  newfile.open(filename, ios::in);
  if (newfile.is_open()) {
    string tp;
    while (getline(newfile, tp)) {
      cout << tp << "\n";
      std::vector<std::string> tokens = split(tp, ": ");
      if (tokens.size() == 2) {
        if (tp.find("name") != std::string::npos) {
          reqName = tokens[1];
        }
        if (tp.find("hs") != std::string::npos) {
          std::vector<std::string> tokens = split(tp, ": ");
          prefix = tokens[1];
        }
      }
    }
    newfile.close();  // close the file object.
  }
  submitReq(reqName, prefix);
}

void insertRoute(std::string prefix, std::string egress) {
  // Define and start event base
  folly::EventBase evb;
  std::thread evbThread([&evb]() { evb.loopForever(); });
  evb.waitUntilRunning();

  auto client =
      openr::getOpenrCtrlPlainTextClient<ddm::thrift::DdmCtrlAsyncClient,
                                         apache::thrift::RocketClientChannel>(
          evb, folly::IPAddress("::1"), 2022, std::chrono::milliseconds(1000),
          std::chrono::milliseconds(5000));

  thrift::UnicastRoute tUnicast;
  tUnicast.dest() = toIpPrefix(prefix);
  thrift::NextHopThrift nexthop;
  // The ip addr of nexthop have no effect
  nexthop.address() = toBinaryAddress(folly::IPAddressV4("1.0.0.1").mask(24));
  nexthop.address()->ifName() = egress;

  tUnicast.nextHops() = std::vector<thrift::NextHopThrift>{nexthop};

  client->semifuture_insertRoute(tUnicast);

  evb.terminateLoopSoon();
  evbThread.join();
}

int main(int argc, char** argv) {
  // Initialize all params
  folly::init(&argc, &argv);

  std::vector<std::string> all_args;

  if (argc > 1) {
    all_args.assign(argv + 1, argv + argc);
  }

  if (all_args[0] == "req") {
    if (all_args[1] == "submit") {
      if (all_args[2] == "file") {
        submitReqFile(all_args[3]);
      } else {
        submitReq(all_args[2], all_args[3]);
      }
    }
  } else if (all_args[0] == "route") {
    if (all_args[1] == "insert") {
      insertRoute(all_args[2], all_args[3]);
    }
  }

  return 0;
}